﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace WhittleList.Bll
{
    public abstract class BaseRepository
    {
        protected IWhittleListDataContext _DBContext {get; private set;}

        protected BaseRepository(IWhittleListDataContext dbcontext)
        {
            this._DBContext = dbcontext;
        }

        #region save changes
        public void SaveChanges(User currentUser)
        {
            if (_DBContext != null)
            {
                BeforeInsertPerformed(_DBContext.GetInserts<User>().ToList(), currentUser);
                BeforeUpdatePerformed(_DBContext.GetUpdates<User>().ToList(), currentUser);

                BeforeInsertPerformed(_DBContext.GetInserts<Site>().ToList(), currentUser);
                BeforeUpdatePerformed(_DBContext.GetUpdates<Site>().ToList(), currentUser);

                BeforeInsertPerformed(_DBContext.GetInserts<Project>().ToList(), currentUser);
                BeforeUpdatePerformed(_DBContext.GetUpdates<Project>().ToList(), currentUser);

                BeforeInsertPerformed(_DBContext.GetInserts<UserProjectRole>().ToList(), currentUser);
                BeforeUpdatePerformed(_DBContext.GetUpdates<UserProjectRole>().ToList(), currentUser);

                BeforeInsertPerformed(_DBContext.GetInserts<Section>().ToList(), currentUser);
                BeforeUpdatePerformed(_DBContext.GetUpdates<Section>().ToList(), currentUser);

                BeforeInsertPerformed(_DBContext.GetInserts<Item>().ToList(), currentUser);
                BeforeUpdatePerformed(_DBContext.GetUpdates<Item>().ToList(), currentUser);
                _DBContext.SubmitChanges();
            }

        }
        #endregion

        #region insert / update users
        private void BeforeInsertPerformed(List<User> users, User currentUser)
        {
            foreach (var inserted in _DBContext.GetInserts<User>())
            {
                if (_DBContext.Users.Where(x => !x.Deleted).Where(existing => existing.EmailAddress == inserted.EmailAddress).Count() != 0)
                {
                    throw new Exceptions.DuplicateEmailException(string.Format("Email Address '{0}' already exists", inserted.EmailAddress));
                }
                else
                {
                    inserted.UTCCreated = inserted.UTCLastUpdated = DateTime.UtcNow;
                }
            }
        }

        private void BeforeUpdatePerformed(List<User> users, User currentUser)
        {
            foreach (var updated in users)
            {
                if (_DBContext.Users.Where(x => !x.Deleted).Where(existing => existing.EmailAddress == updated.EmailAddress && existing.Id != updated.Id).Count() != 0)
                {
                    throw new Exceptions.DuplicateEmailException(string.Format("Email Address '{0}' already exists", updated.EmailAddress));
                }
                else
                {
                    updated.UTCLastUpdated = DateTime.UtcNow;
                }
            }
        }
        #endregion

        #region insert / update sites
        private void BeforeInsertPerformed(List<Site> sites, User currentUser)
        {
            foreach (var site in sites)
            {
                if (_DBContext.Sites.Where(x => !x.Deleted).Where(x => x.Name == site.Name).Count() != 0)
                {
                    throw new Exceptions.DuplicateNameException(string.Format("A site with the name '{0}' already exists", site.Name));
                }
                else if (_DBContext.Sites.Where(x => !x.Deleted).Where(x => x.Url == site.Url).Count() != 0)
                {
                    throw new Exceptions.DuplicateUrlException(string.Format("A site with the url '{0}' already exists", site.Url));
                }
                else
                {
                    site.OnInserting(currentUser);
                }
            }
        }

        private void BeforeUpdatePerformed(List<Site> sites, User currentUser)
        {
            foreach (var site in sites)
            {
                if (_DBContext.Sites.Where(x => !x.Deleted).Where(x => x.Name == site.Name && x.Id != site.Id).Count() != 0)
                {
                    throw new Exceptions.DuplicateNameException(string.Format("A site with the name '{0}' already exists", site.Name));
                }
                else if (_DBContext.Sites.Where(x => !x.Deleted).Where(x => x.Url == site.Url && x.Id != site.Id).Count() != 0)
                {
                    throw new Exceptions.DuplicateUrlException(string.Format("A site with the url '{0}' already exists", site.Url));
                }
                else
                {
                    site.OnUpdating(currentUser);
                }
            }
        }
        #endregion

        #region insert / update projects
        private void BeforeInsertPerformed(List<Project> projects, User currentUser)
        {
            foreach (var project in projects)
            {
                if (_DBContext.Projects.Where(x => !x.Deleted).Where(x => x.Name == project.Name && x.SiteId == project.SiteId).Count() != 0)
                {
                    throw new Exceptions.DuplicateNameException(string.Format("A project with the name '{0}' already exists for this site", project.Name));
                }
                else
                {
                    project.OnInerting(currentUser);
                }
            }
        }

        private void BeforeUpdatePerformed(List<Project> projects, User currentUser)
        {
            foreach (var project in projects)
            {
                if (_DBContext.Projects.Where(x => !x.Deleted).Where(x => x.Name == project.Name && x.SiteId == project.SiteId && x.Id != project.Id).Count() != 0)
                {
                    throw new Exceptions.DuplicateNameException(string.Format("A project with the name '{0}' already exists for this site", project.Name));
                }
                else
                {
                    project.OnUpdating(currentUser);
                }
            }
        }
        #endregion

        #region insert / update userprojectrole
        private void BeforeInsertPerformed(List<UserProjectRole> projectRoles, User currentUser)
        {
            foreach (var projectRole in projectRoles)
            {
                if (_DBContext.UserProjectRoles.Where(x => !x.Deleted).Where(x => x.ProjectId == projectRole.ProjectId && x.UserId == projectRole.UserId).Count() != 0)
                {
                    throw new Exceptions.AlreadyExistsException(string.Format("A project role for inserted '{0}' already exists for project '{1}", projectRole.UserId, projectRole.ProjectId));
                }
                else
                {
                    projectRole.OnInserting(currentUser);
                }
            }
        }

        private void BeforeUpdatePerformed(List<UserProjectRole> projectRoles, User currentUser)
        {
            foreach (var projectRole in projectRoles)
            {
                if (_DBContext.UserProjectRoles.Where(x => !x.Deleted).Where(x => x.ProjectId == projectRole.ProjectId && x.UserId == projectRole.UserId && x.Id != projectRole.Id).Count() != 0)
                {
                    throw new Exceptions.AlreadyExistsException(string.Format("A project role for inserted '{0}' already exists for project '{1}", projectRole.UserId, projectRole.ProjectId));
                }
                else
                {
                    projectRole.OnUpdating(currentUser);
                }
            }
        }
        #endregion

        #region insert / update section
        private void BeforeInsertPerformed(List<Section> sections, User currentUser)
        {
            foreach (var section in sections)
            {
                if (_DBContext.Sections.Where(x => !x.Deleted).Where(x => x.ProjectId == section.ProjectId && x.Name == section.Name).Count() != 0)
                {
                    throw new Exceptions.AlreadyExistsException(string.Format("A section with name '{0}' already exists for project '{1}", section.Name, section.ProjectId));
                }
                else
                {
                    section.OnInserting(currentUser);
                }
            }
        }

        private void BeforeUpdatePerformed(List<Section> sections, User currentUser)
        {
            foreach (var section in sections)
            {
                if (_DBContext.Sections.Where(x => !x.Deleted).Where(x => x.ProjectId == section.ProjectId && x.Name == section.Name && x.Id != section.Id).Count() != 0)
                {
                    throw new Exceptions.AlreadyExistsException(string.Format("A section with name '{0}' already exists for project '{1}", section.Name, section.ProjectId));
                }
                else
                {
                    section.OnUpdating(currentUser);
                }
            }
        }
        #endregion


        #region insert / update item
        private void BeforeInsertPerformed(List<Item> items, User currentUser)
        {
            foreach (var item in items)
            {
                item.OnInserting(currentUser);
            }
        }

        private void BeforeUpdatePerformed(List<Item> items, User currentUser)
        {
            foreach (var item in items)
            {
                item.OnUpdating(currentUser);
            }
        }
        #endregion

    }
}
